
// Main
#include "../Main.h"

// My header
#include "TreeEditor.h"

// Fridge
#include "../Controller/Controller_Main.h"
#include "../Input.h"
#include "../MathsHelper.h"
#include "../StringHash.h"
#include "../TextRender.h"
#include "../TypedList.h"
#include "../SuperTree.h"

static const u_int uTREEEDITOR_ENTRY_HEIGHT = 32;
static const u_int uTREEEDITOR_ENTRY_IMAGE_SIZE = 24;
static const u_int uTREEEDITOR_ENTRY_BUTTON_SIZE = 24;
static const u_int uTREEEDITOR_ENTRY_MARGINX = 5;
static const u_int uTREEEDITOR_ENTRY_INDENT = 20;
static const u_int uTREEEDITOR_ENTRY_TEXTCOLOUR = 0x777777FF; 
static const u_int uTREEEDITOR_INSERTION_MARK_COL = 0x888888FF;
static const u_int uTREEEDITOR_TREE_CONNECTION_COL = 0x666666FF;

// =======================================================
//                   TreeEditorEntry
// =======================================================

TreeEditorEntry::TreeEditorEntry() : Panel( 0 )
{
	m_bIsContainer = false;
	m_bIsExpanded = false;
	m_bUserCanDelete = false;
	m_bUserCanDuplicate = false;

	m_bIsFlaggedToDelete = false;
	m_bIsFlaggedToDuplicate = false;

	m_uIndentation = 0;

	m_pxImage = 0;
	m_pxLabel = 0;
	memset( m_pxButtons, 0, sizeof( Button* ) * NUM_BUTTONS );
}

TreeEditorEntry::~TreeEditorEntry()
{
}

void TreeEditorEntry::Render ( bool bSelected )
{
	if( bSelected )
	{
		// We are selected
		SetColour( 0xE0E0E0FF );
	}
	else
	{
		if( MouseInWidget() && !DragDropManager::IsDragging() )
		{
			// We are mousing over
			SetColour( 0xEEEEEEFF );
		}
		else
		{
			// Not mousing over (or mousing over but drag-dropping)
			SetColour( 0xFFFFFFFF );
		}
	}

	Panel::Render();
}

void TreeEditorEntry::Update ( )
{
	if( !DragDropManager::IsDragging() )
	{
		for( u_int uButton = 0; uButton < NUM_BUTTONS; ++uButton )
		{
			if( m_pxButtons[ uButton ] )
			{
				m_pxButtons[ uButton ]->SetVisible( MouseInWidget() );
			}
		}
	}

	Panel::Update();
}

void TreeEditorEntry::SetImage ( const u_int uTextureHash )
{
	if( !m_pxImage )
	{
		m_pxImage = new Panel( this );
		m_pxImage->SetSize( uTREEEDITOR_ENTRY_IMAGE_SIZE, uTREEEDITOR_ENTRY_IMAGE_SIZE );
	}

	m_pxImage->SetImage( uTextureHash );
}

void TreeEditorEntry::SetText( const char* szText )
{
	if( !m_pxLabel )
	{
		m_pxLabel = new StaticText( this );
		m_pxLabel->SetAutoSizes( true );
	}

	m_pxLabel->SetTextColour( uTREEEDITOR_ENTRY_TEXTCOLOUR );
	m_pxLabel->SetText( szText );
}

void TreeEditorEntry::SetIsContainer ( const bool bIsContainer )
{
	if( m_bIsContainer == bIsContainer )
	{
		return;
	}

	m_bIsContainer = bIsContainer;

	if( m_bIsContainer )
	{
		if( !m_pxButtons[ BUTTON_EXPAND ] )
		{
			m_pxButtons[ BUTTON_EXPAND ] = new Button( this );
			m_pxButtons[ BUTTON_EXPAND ]->SetSize( uTREEEDITOR_ENTRY_BUTTON_SIZE, uTREEEDITOR_ENTRY_BUTTON_SIZE );
			m_pxButtons[ BUTTON_EXPAND ]->SetButtonPressCallback( ExpandButtonPressed, this );
			m_pxButtons[ BUTTON_EXPAND ]->SetImage( GetStringHash( "Expand32" ) );
			m_pxButtons[ BUTTON_EXPAND ]->SetVisible( false );
		}
	}
	else
	{
		if( m_pxButtons[ BUTTON_EXPAND ] )
		{
			delete m_pxButtons[ BUTTON_EXPAND ];
			m_pxButtons[ BUTTON_EXPAND ] = 0;
		}
	}

	RepositionButtons();
}

void TreeEditorEntry::SetUserCanDelete ( const bool bCanDelete )
{
	if( m_bUserCanDelete == bCanDelete )
	{
		return;
	}

	m_bUserCanDelete = bCanDelete;

	if( m_bUserCanDelete )
	{
		if( !m_pxButtons[ BUTTON_DELETE ] )
		{
			m_pxButtons[ BUTTON_DELETE ] = new Button( this );
			m_pxButtons[ BUTTON_DELETE ]->SetSize( uTREEEDITOR_ENTRY_BUTTON_SIZE, uTREEEDITOR_ENTRY_BUTTON_SIZE );
			m_pxButtons[ BUTTON_DELETE ]->SetButtonPressCallback( DeleteButtonPressed, this );
			m_pxButtons[ BUTTON_DELETE ]->SetImage( GetStringHash( "Delete32" ) );
			m_pxButtons[ BUTTON_DELETE ]->SetVisible( false );
		}
	}
	else
	{
		if( m_pxButtons[ BUTTON_DELETE ] )
		{
			delete m_pxButtons[ BUTTON_DELETE ];
			m_pxButtons[ BUTTON_DELETE ] = 0;
		}
	}


	RepositionButtons();
}

void TreeEditorEntry::SetUserCanDuplicate ( const bool bCanDuplicate )
{
	if( m_bUserCanDuplicate == bCanDuplicate )
	{
		return;
	}

	m_bUserCanDuplicate = bCanDuplicate;

	if( m_bUserCanDuplicate )
	{
		if( !m_pxButtons[ BUTTON_DUPLICATE ] )
		{
			m_pxButtons[ BUTTON_DUPLICATE ] = new Button( this );
			m_pxButtons[ BUTTON_DUPLICATE ]->SetSize( uTREEEDITOR_ENTRY_BUTTON_SIZE, uTREEEDITOR_ENTRY_BUTTON_SIZE );
			m_pxButtons[ BUTTON_DUPLICATE ]->SetButtonPressCallback( DuplicateButtonPressed, this );
			m_pxButtons[ BUTTON_DUPLICATE ]->SetImage( GetStringHash( "Duplicate32" ) );
			m_pxButtons[ BUTTON_DUPLICATE ]->SetVisible( false );
		}
	}
	else
	{
		if( m_pxButtons[ BUTTON_DUPLICATE ] )
		{
			delete m_pxButtons[ BUTTON_DUPLICATE ];
			m_pxButtons[ BUTTON_DUPLICATE ] = 0;
		}
	}


	RepositionButtons();
}


void TreeEditorEntry::RepositionButtons ( )
{
	const u_int uYPos = m_uPositionY + m_uHeight / 2 - uTREEEDITOR_ENTRY_BUTTON_SIZE / 2;
	u_int uXPos = m_uPositionX + m_uWidth - ( uTREEEDITOR_ENTRY_MARGINX + uTREEEDITOR_ENTRY_BUTTON_SIZE );

	for( u_int uButton = 0; uButton < NUM_BUTTONS; ++uButton )
	{
		if( m_pxButtons[ uButton ] && m_pxButtons[ uButton ]->IsVisible() )
		{
			m_pxButtons[ uButton ]->SetPosition( uXPos, uYPos );

			uXPos -= ( uTREEEDITOR_ENTRY_BUTTON_SIZE + uTREEEDITOR_ENTRY_MARGINX );
		}
	}
}

void TreeEditorEntry::ExpandButtonPressed ( void* pUserData )
{
	TreeEditorEntry* pxThis = reinterpret_cast<TreeEditorEntry*>( pUserData );
	if( pxThis )
	{
		pxThis->_ExpandButtonPressed();
	}
}

void TreeEditorEntry::_ExpandButtonPressed ( )
{
	m_bIsExpanded = !m_bIsExpanded;

	if( m_bIsExpanded )
	{
		m_pxButtons[ BUTTON_EXPAND ]->SetImage( GetStringHash( "Retract32" ) );
	}
	else
	{
		m_pxButtons[ BUTTON_EXPAND ]->SetImage( GetStringHash( "Expand32" ) );
	}
}

void TreeEditorEntry::DeleteButtonPressed ( void* pUserData )
{
	TreeEditorEntry* pxThis = reinterpret_cast<TreeEditorEntry*>( pUserData );
	if( pxThis )
	{
		pxThis->_DeleteButtonPressed();
	}
}

void TreeEditorEntry::_DeleteButtonPressed ( )
{
	m_bIsFlaggedToDelete = true;
}

void TreeEditorEntry::DuplicateButtonPressed ( void* pUserData )
{
	TreeEditorEntry* pxThis = reinterpret_cast<TreeEditorEntry*>( pUserData );
	if( pxThis )
	{
		pxThis->_DuplicateButtonPressed();
	}
}

void TreeEditorEntry::_DuplicateButtonPressed ( )
{
	m_bIsFlaggedToDuplicate = true;
}

void TreeEditorEntry::SetIndentation ( const u_int uIndent )
{
	m_uIndentation = uIndent;
}

void TreeEditorEntry::SetPosition ( const u_int uX, const u_int uY )
{
	Panel::SetPosition( uX, uY );

	RepositionButtons();

	if( m_pxImage )
	{
		m_pxImage->SetPosition( m_uPositionX + uTREEEDITOR_ENTRY_MARGINX, m_uPositionY + m_uHeight / 2 - uTREEEDITOR_ENTRY_IMAGE_SIZE / 2 );
	}

	if( m_pxLabel )
	{
		m_pxLabel->SetPosition( m_uPositionX + 2 * uTREEEDITOR_ENTRY_MARGINX + uTREEEDITOR_ENTRY_IMAGE_SIZE, m_uPositionY + m_uHeight / 2 - m_pxLabel->GetResizedHeight() / 2 );
	}
}

bool TreeEditorEntry::MouseClickShouldSelect() const
{
	// Check that the mouse is in the widget and not over any sub controls like buttons
	if( !MouseInWidget() )
	{
		return false;
	}
	
	for( u_int uButton = 0; uButton < NUM_BUTTONS; ++uButton )
	{
		if( m_pxButtons[ uButton ] && m_pxButtons[ uButton ]->MouseInWidget() )
		{
			return false;
		}
	}

	return true;
}

// =======================================================
//                       TreeEditor
// =======================================================

TreeEditor::TreeEditor( Widget* pxParent ) : Panel( pxParent )
{
	// Create the tree of editor entries
	m_pxTreeEditorItems = new SuperTree<TreeEditorEntry>( true );
	m_xSelectedEntry = INVALID_HANDLE;
	m_xInsertionMarker = INVALID_HANDLE;

	SetHasBorder( true );

	m_pfnOnDeleteCallback = 0;
	m_pfnOnDuplicateCallback = 0;
	m_pfnOnSelectionCallback = 0;
}

TreeEditor::~TreeEditor()
{
	delete m_pxTreeEditorItems;
}

bool TreeEditor::RenderItems_RecurseCallback( const TreeEditorEntry* pxEntry )
{
	return ( pxEntry->IsExpanded() );
}

void TreeEditor::Clear()
{
	delete m_pxTreeEditorItems;
	m_pxTreeEditorItems = new SuperTree<TreeEditorEntry>( true );

	m_xSelectedEntry = INVALID_HANDLE;
	m_xInsertionMarker = INVALID_HANDLE;
}

void TreeEditor::Render ( )
{
	Panel::Render();

	u_int uPosY = GetResizedY();
	
	SuperTreeWalker<TreeEditorEntry> xWalker( m_pxTreeEditorItems );
	xWalker.SetShouldRecurseCallback( RenderItems_RecurseCallback );

	while( !xWalker.Done() )
	{
		TreeEditorEntry* pxEntry = xWalker.Get();

		const u_int uIndent = m_pxTreeEditorItems->GetNodeDepth( xWalker.GetHandle() );

		RenderEntry( pxEntry, uIndent, uPosY, ( xWalker.GetHandle() == m_xSelectedEntry ) );
		if( xWalker.GetHandle() == m_xInsertionMarker )
		{
			const u_int uHeight = 3;
			const u_int uSwappedBackCol = MathsHelper::RGBAtoABGR( uTREEEDITOR_INSERTION_MARK_COL );
			glColor4ubv( reinterpret_cast<const GLubyte*>( &uSwappedBackCol ) );

			glBindTexture( GL_TEXTURE_2D, 0 );
			
			glBegin( GL_QUADS );

			glVertex2i( pxEntry->GetResizedX(), pxEntry->GetResizedY() + pxEntry->GetResizedHeight() );
			glVertex2i( pxEntry->GetResizedX() + pxEntry->GetResizedWidth(), pxEntry->GetResizedY() + pxEntry->GetResizedHeight() );
			glVertex2i( pxEntry->GetResizedX() + pxEntry->GetResizedWidth(), pxEntry->GetResizedY() + pxEntry->GetResizedHeight() + uHeight );
			glVertex2i( pxEntry->GetResizedX(), pxEntry->GetResizedY() + pxEntry->GetResizedHeight() + uHeight );

			glEnd();

			uPosY += uHeight;
		}

		xWalker.Next();
	}
}

void TreeEditor::Update ( )
{
	Panel::Update();
	
	const Input* pxInputHandler = Controller_Main::GetInputHandler();

	// Loop over updating entries

	FHandle xToDelete = INVALID_HANDLE;
	FHandle xToDuplicate = INVALID_HANDLE;

	SuperTreeWalker<TreeEditorEntry> xWalker( m_pxTreeEditorItems );
	xWalker.SetShouldRecurseCallback( RenderItems_RecurseCallback );

	while( !xWalker.Done() )
	{
		TreeEditorEntry* pxEntry = xWalker.Get();

		if( pxEntry )
		{
			pxEntry->Update();

			// Check if an entry is to be removed. This only handles a single deletion per frame
			// but it is based on a user clicking a button so we dont need to FHandle any more.
			if( pxEntry->IsFlaggedToDelete() )
			{
				xToDelete = xWalker.GetHandle();
			}
			else if( pxEntry->IsFlaggedToDuplicate() )
			{
				pxEntry->ClearDuplicateFlag();
				xToDuplicate = xWalker.GetHandle();
			}
			
			if( pxInputHandler->MouseButtonPressed( INPUT_MOUSE_BUTTON_LEFT ) && pxEntry->MouseClickShouldSelect() )
			{
				SetSelection( xWalker.GetHandle() );
			}
		}

		xWalker.Next();
	}

	if( xToDelete != INVALID_HANDLE )
	{
		m_pxTreeEditorItems->Delete( xToDelete );
		if( m_pfnOnDeleteCallback )
		{
			m_pfnOnDeleteCallback( xToDelete );
		}
		if( m_xSelectedEntry == xToDelete )
		{
			SetSelection( INVALID_HANDLE );
		}
	}
	if( xToDuplicate != INVALID_HANDLE )
	{
		// Todo: duplicate
	}
}

FHandle TreeEditor::GetEntryUnderCursor()
{
	SuperTreeWalker<TreeEditorEntry> xWalker( m_pxTreeEditorItems );

	while( !xWalker.Done() )
	{
		TreeEditorEntry* pxEntry = xWalker.Get();

		if( pxEntry )
		{
			if( pxEntry->MouseInWidget() )
			{
				return xWalker.GetHandle();
			}
		}

		xWalker.Next();
	}

	return INVALID_HANDLE;
}

FHandle TreeEditor::GetLastEntry()
{
	SuperTreeIter<TreeEditorEntry> xIter( m_pxTreeEditorItems );
	
	FHandle xLast = INVALID_HANDLE;

	while( !xIter.Done() )
	{
		xLast = xIter.GetHandle();
		xIter.Next();
	}

	return xLast;
}

void TreeEditor::SetInsertionMarker( FHandle xHandle )
{
	m_xInsertionMarker = xHandle;
}

void TreeEditor::ClearInsertionMarker()
{
	m_xInsertionMarker = INVALID_HANDLE;
}

FHandle TreeEditor::InsertTreeItem ( const char* szLabel, const FHandle xSpecifyHandle )
{
	TreeEditorEntry* pxNewEntry = new TreeEditorEntry();

	pxNewEntry->SetText( szLabel );

	FHandle xHandle = INVALID_HANDLE;

	xHandle = m_pxTreeEditorItems->Add( pxNewEntry, TREE_HANDLE_HEAD, xSpecifyHandle );

	return xHandle;
}

void TreeEditor::MoveTreeItem( const FHandle xHandle, const FHandle xNewParent, const FHandle xMoveAfter )
{
	m_pxTreeEditorItems->Move( xHandle, xNewParent, xMoveAfter );
}

TreeEditorEntry* TreeEditor::GetTreeItem( const FHandle xHandle )
{
	return m_pxTreeEditorItems->Get( xHandle );
}

void TreeEditor::RenderEntry ( TreeEditorEntry* pxEntry, u_int uIndent, u_int& uYPos, bool bSelected )
{
	if( !pxEntry )
	{
		return;
	}

	const u_int uIndentDepth = ( uIndent * uTREEEDITOR_ENTRY_INDENT );

	// Render myself
	pxEntry->SetSize( GetResizedWidth() - ( uIndentDepth + 2 ), uTREEEDITOR_ENTRY_HEIGHT );
	pxEntry->SetPosition( GetResizedX() + 1 + uIndentDepth, uYPos + 1 );
	pxEntry->SetIndentation( uIndent );
	pxEntry->Render( bSelected );

	// Render a tree connection if indented
	if( uIndent != 0 )
	{
		const float fPositionX = static_cast< float >( pxEntry->GetResizedX() );
		const float fPositionY = static_cast< float >( pxEntry->GetResizedY() );
		const float fHeight = static_cast< float >( pxEntry->GetResizedHeight() );

		glBindTexture( GL_TEXTURE_2D, INVALID_TEXTURE_ID );

		u_int uSwapped = MathsHelper::RGBAtoABGR( uTREEEDITOR_TREE_CONNECTION_COL );

		glColor4ubv( reinterpret_cast<GLubyte*>( &uSwapped ) );

		glEnable( GL_LINE_SMOOTH );
		glLineWidth( 2.0f );

		glBegin( GL_LINE_STRIP );

		glVertex2f( fPositionX, fPositionY + fHeight * 0.5f );
		glVertex2f( fPositionX - (float)uTREEEDITOR_ENTRY_INDENT * 0.5f,  fPositionY + fHeight * 0.5f );
		glVertex2f( fPositionX - (float)uTREEEDITOR_ENTRY_INDENT * 0.5f,  fPositionY );

		glEnd( );
	}

	uYPos += uTREEEDITOR_ENTRY_HEIGHT;
}

void TreeEditor::SetSelection( const FHandle xHandle )
{ 
	m_xSelectedEntry = xHandle;
	if( m_pfnOnSelectionCallback )
	{
		m_pfnOnSelectionCallback( xHandle );
	}
}

FHandle TreeEditor::GetSelection()
{
	return m_xSelectedEntry;
}

void TreeEditor::SetOnDeleteCallback( TreeEditorActionCallback pfnCallback )
{
	m_pfnOnDeleteCallback = pfnCallback;
}

void TreeEditor::SetOnDuplicateCallback( TreeEditorActionCallback pfnCallback )
{
	m_pfnOnDuplicateCallback = pfnCallback;
}

void TreeEditor::SetOnSelectionCallback( TreeEditorActionCallback pfnCallback )
{
	m_pfnOnSelectionCallback = pfnCallback;
}

//eof
